iT邦幫忙

2022 iThome 鐵人賽

DAY 13
1
Modern Web

派對動物嗨起來!系列 第 13

D13 - 來布置一下吧

  • 分享至 

  • xImage
  •  

本系列文已改編成書「甚麼?網頁也可以做派對遊戲?使用 Vue 和 babylon.js 打造 3D 派對遊戲吧!」

書中不只重構了程式架構、改善了介面設計,還新增了 2 個新遊戲呦!ˋ( ° ▽、° )

新遊戲分別使用了陀螺儀與震動回饋,趕快買書來研究研究吧!ლ(╹∀╹ლ)

在此感謝深智數位的協助,歡迎大家前往購書,鱈魚感謝大家 (。・∀・)。

助教:「所以到底差在哪啊?沒圖沒真相,被你坑了都不知道。(´。_。`)」

鱈魚:「你對我是不是有甚麼很深的偏見啊 (っ °Д °;)っ,來人啊,上連結!」

Yes


貼個壁紙吧

一片空白的遊戲大廳,真是讓人覺得開了個寂寞派對,讓我們來打造一個背景吧!( •̀ ω •́ )✧

新增 background-polygons-pattern 組件。

src\components\background-polygons-pattern.vue

<template>
</template>

<script setup lang="ts">
import { ref } from 'vue';

interface Props {
  label?: string;
}
const props = withDefaults(defineProps<Props>(), {
  label: '',
});
</script>

<style scoped lang="sass">
</style>

把 background-polygons-pattern 做為背景,加到 game-console-lobby 中。

src\views\game-console-lobby.vue

<template>
  <background-polygons-pattern class="absolute inset-0" />
</template>

<script setup lang="ts">
import BackgroundPolygonsPattern from '../components/background-polygons-pattern.vue';

import { useLoading } from '../composables/use-loading';

const loading = useLoading();
loading.hide();

</script>

<style scoped lang="sass">
</style>

接著先定義一下 Props。

interface Props {
  mainColor?: string;
}
const props = withDefaults(defineProps<Props>(), {
  mainColor: '#b3e6d0',
});

接著一樣是新增 backgroundStyle,產生背景顏色。

const backgroundStyle = computed(() => {
  // 變亮
  const lightenColor = lighten(props.mainColor, 10);

  // 變暗並偏移色相
  const darkColor = lighten(props.mainColor, -10);

  const hsvColor = rgbToHsv(textToRgb(darkColor));
  hsvColor.h -= 20;

  const offsetColor = rgbToHex(hsvToRgb(hsvColor));

  return {
    background: `linear-gradient(-30deg, ${offsetColor}, ${props.mainColor}, ${lightenColor}, ${props.mainColor}, ${offsetColor})`
  }
});

並在 template 加入最外層 div 並綁定 style。

<template>
  <div
    class="overflow-hidden"
    :style="backgroundStyle"
  >
  </div>
</template>
...

<script setup lang="ts">
import { computed } from 'vue';
import { colors } from 'quasar';

const { lighten, textToRgb, rgbToHsv, hsvToRgb, rgbToHex } = colors;

...

const backgroundStyle = computed(() => { ... });
</script>

...

顏色出現了,終於不是全白的寂寞派對了!(≧∇≦)ノ

Untitled

接著準備加入裝飾,預期效果是網點壁紙的感覺,直接拿先前的 polygon-base 組件來用吧。

產生多邊形。

import PolygonBase, { ShapeType } from './polygon-base.vue';

const shapeTypes = Object.values(ShapeType);
const polygons: ShapeType[] = [];
for (let i = 0; i < 500; i++) {
  polygons.push(shapeTypes[i % shapeTypes.length]);
}

在模板產生多邊形。

<template>
  <div
    ...
  >
    <div class="pattern flex w-full h-full gap-10">
      <polygon-base
        v-for="(shape, i) in polygons"
        :key="i"
        size="2rem"
        fill="solid"
        :shape="shape"
        color="#f9fff0"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
...
import PolygonBase, { ShapeType } from './polygon-base.vue';
...

const shapeTypes = Object.values(ShapeType);
const polygons: ShapeType[] = [];
for (let i = 0; i < 500; i++) {
  polygons.push(shapeTypes[i % shapeTypes.length]);
}

</script>

有了。( ´◡` )

Untitled

接著加點動畫效果吧。

<template>
  <div
    ...
  >
    <div class="pattern flex w-full h-full gap-10">
      <polygon-base
        v-for="(shape, i) in polygons"
        ...
        class="polygon"
        ...
      />
    </div>
  </div>
</template>
...
<style scoped lang="sass">
.pattern
  transform: rotate(-6deg) translate(2%, -5%)

.polygon
  animation: polygon 3s infinite

@keyframes polygon
  0%, 100%
    opacity: 0.2
  30%, 70%
    opacity: 0.4
</style>

仔細看會發現多邊形有波浪舞的感覺。✧*。٩(ˊᗜˋ*)و✧*。

ezgif-1-2a6829cdbc.gif

加上基本元素吧

接著讓我們加上主選單、玩家頭像、房間 ID 和遊戲選擇面板吧

建立以下組件:

  • room-id-chip:顯示房間 ID
  • player-avatar:呈現玩家頭像
  • game-tab-panel:提供目前遊戲清單

先加上基本外觀與樣式,再慢慢完成細節。

src\components\room-id-chip.vue

<template>
  <div class="room-id py-8 px-16 rounded-full">
    {{ roomId }}
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';

import { useGameConsoleStore } from '../stores/game-console.store';

interface Props {
  color?: string;
}
const props = withDefaults(defineProps<Props>(), {
  color: '#111',
});

const gameConsoleStore = useGameConsoleStore();

const roomId = computed(() => gameConsoleStore.roomId);
</script>

<style scoped lang="sass">
.room-id
  background: rgba(white, 0.4)
  color: v-bind('props.color')
  font-size: 2rem
  letter-spacing: 2px
  font-weight: 700
</style>

src\components\player-avatar.vue

<template>
  <q-avatar
    color="white"
    text-color="black"
    size="6rem"
  >
    {{ props.codeName }}
  </q-avatar>
</template>

<script setup lang="ts">
interface Props {
  /** 玩家 ID,同 client ID */
  playerId: string;
  /** 玩家代號 */
  codeName: string;
}
const props = withDefaults(defineProps<Props>(), {});
</script>

<style scoped lang="sass">
</style>

src\components\game-tab-panel.vue

<template>
  <div class="panel relative rounded-[3rem]">
  </div>
</template>

<script setup lang="ts">
</script>

<style scoped lang="sass">
.panel
  width: 84vmin
  height: 84vmin
  background: rgba(white, 0.8)
  overflow: hidden
  border: 1rem solid white
</style>

完成 game-console-lobby 基本內容結構。

src\views\game-console-lobby.vue

<template>
  <background-polygons-pattern class="absolute inset-0" />

  <div class="absolute inset-0 flex">
    <div class="flex w-full h-full">
      <!-- 選單 -->
      <div class="w-1/3 flex flex-col p-12">
        <div class="flex flex-col flex-1 justify-center items-center gap-14">
          <room-id-chip color="#67785d" />

          <btn-base
            label="開始遊戲"
            class="w-96"
            label-hover-color="#7b916e"
            stroke-hover-color="white"
          />

          <btn-base
            label="結束派對"
            class="w-96"
            label-hover-color="#7b916e"
            stroke-hover-color="white"
          />
        </div>

        <!-- 玩家清單 -->
        <transition-group
          name="list"
          tag="div"
          class="flex justify-center items-center gap-4 h-32"
        >
          <player-avatar
            player-id="id"
            code-name="1P"
          />
        </transition-group>
      </div>

      <!-- 選擇遊戲 -->
      <div class="w-2/3 flex flex-nowrap justify-between items-center flex-1 px-16">
        <btn-base label="◀" />
        <game-tab-panel />
        <btn-base label="▶" />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import BackgroundPolygonsPattern from '../components/background-polygons-pattern.vue';
import GameTabPanel from '../components/game-tab-panel.vue';
import PlayerAvatar from '../components/player-avatar.vue';
import RoomIdChip from '../components/room-id-chip.vue';
import BtnBase from '../components/btn-base.vue';
...
</script>
...

現在看起來應該會長這樣。

Untitled

看起來終於有一點遊戲大廳的樣子了!✧*。٩(ˊᗜˋ*)و✧*。

總結

  • 完成 background-polygons-pattern
  • 新增 room-id-chip 組件
  • 新增 player-avatar 組件
  • 新增 game-tab-panel 組件
  • 完成 game-console-lobby template 基本內容

以上程式碼已同步至 GitLab,大家可以前往下載:

GitLab - D13


上一篇
D12 - 歡迎光臨遊戲大廳:使用 Vue Router 切換頁面
下一篇
D14 - 裝飾大廳細節
系列文
派對動物嗨起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言